package com.apes.fn.scm.internal.help.service;

import com.alibaba.fastjson.JSON;
import com.apes.fn.scm.internal.help.model.InternalHelpReturnApproval;
import com.apes.fn.scm.internal.help.model.InternalHelpReturnApprovalItem;
import com.apes.fn.scm.internal.help.service.voucher.ReturnAccountService;
import com.apes.fn.scm.retailOrder.model.RetailOrder;
import com.apes.fn.scm.retailReturn.RetailReturn;
import com.apes.fn.scm.retailReturn.RetailReturnItem;
import com.apes.fn.scm.retailReturn.RetailReturnRepository;
import com.apes.fn.server.servicePlan.model.ServicePlanItem;
import com.apes.fn.server.servicePlan.repository.ServicePlanItemRepository;
import com.apes.fn.server.workOrder.model.MaintenanceOrder;
import com.apes.fn.server.workOrder.repository.MaintenanceOrderRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.outbox.OutboxEventService;
import com.apes.framework.plugin.outbox.OutboxPayloadWrapper;
import com.apes.framework.plugin.soe.store.database.repository.SceneRepository;
import com.apes.framework.util.LogicalUtil;
import com.apes.framework.util.MapUtil;
import com.apes.scm.account.account.model.InventoryBatch;
import com.apes.scm.contract.model.Contract;
import com.apes.scm.masterdata.enumeration.model.Enumeration;
import com.apes.scm.masterdata.stock.model.Location;
import com.apes.scm.masterdata.stock.repository.LocationRepository;
import com.apes.scm.sal.saleOrder.SaleOrder;
import com.apes.scm.sal.saleOrder.SaleOrderItem;
import com.apes.scm.sal.saleOrder.SaleOrderItemRepository;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherInventory;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherInventoryRepository;
import com.apes.scm.voucher.repository.VoucherItemRepository;
import com.apes.scm.voucher.repository.VoucherRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author wuq
 * @Date 2021-10-14
 */
@Service
public class InternalHelpReturnStartService extends DomainService {

    @Autowired
    private ReturnAccountService returnAccountService;
    @Autowired
    private ServicePlanItemRepository servicePlanItemRepository;
    @Autowired
    private VoucherRepository voucherRepository;
    @Autowired
    private MaintenanceOrderRepository maintenanceOrderRepository;
    @Autowired
    private VoucherItemRepository voucherItemRepository;

    public void createById(SimpleRequest request) {
        String id = request.get("id");
        RetailReturn retailReturn = SpringManager.getBean(RetailReturnRepository.class).findOne(id);
        process(retailReturn);
    }

    public void process(RetailReturn retailReturn) {
        // 第一步：检查退异业合作
        returnAccountService.returnCrossIC(retailReturn);

        // 第二步：检查油漆退货（本门店接车油漆服务）
        returnAccountService.returnRetailPaint(retailReturn);

        // 第三步：检查退代销外协部分、年审部分(代销)
        returnAccountService.returnRetailOutHelp(retailReturn);

        // 第四步：检查退经销内协部分
        returnServicePlanHelp(retailReturn);

        // 第五步：检查委托定损退对保险公司销售
        returnAccountService.returnEntrustLoss(retailReturn);

        // 第六步：检查是否为单位客户挂账
        returnAccountService.returnUnitClient(retailReturn);
    }

    private void returnServicePlanHelp(RetailReturn retailReturn) {
        // 经销部分区分外协、同公司内协、同公司内协油漆、跨公司内协
        List<RetailReturnItem> itemList = retailReturn.getSaleItems().stream()
                .filter(retailReturnItem -> LogicalUtil.in(retailReturnItem.getRetailOrderItem().getProductRule(), 20))
                .collect(Collectors.toList());
        if (itemList.isEmpty()) return;

        // 开始区分
        HashMap<RetailReturnItem, ServicePlanItem> itemMap = new HashMap();     // 构造出退货明细对应的方案明细
        itemList.forEach(retailReturnItem -> itemMap.put(retailReturnItem, findByServicePlanItemId(retailReturnItem.getRetailOrderItem().getOriginnumber())));
        //检查服务方案明细类型，区分是否内协的部分
        Map<Boolean, List<Map.Entry<RetailReturnItem, ServicePlanItem>>> acceptanceCheck = servicePlanItemInnerCheck(itemMap);
        acceptanceCheck.forEach((isInnerHelp, list) -> {
            if (isInnerHelp) {  // 内协部分区分
                innerHelpProcess(list);
            } else {
                nonInnerHelpProcess(list);      // 非内协的部分
            }
        });
    }

    private ServicePlanItem findByServicePlanItemId(String id) {
        ServicePlanItem servicePlanItem = servicePlanItemRepository.findOne(id);
        Optional.ofNullable(servicePlanItem).orElseThrow(() -> new RuntimeException(String.format("根据零售明细单来源行号[%s]获取服务方案明细行失败！", id)));
        return servicePlanItem;
    }

    /**
     * 非内协部分处理（工单外协部分）
     * 如果是经销带安，就说明一定是存在内协或者外协（本公司外协）
     * 服务方案明细中会记录当前商品是，一般，内协，外协
     *
     * @param list List<Map.Entry<RetailReturnItem, ServicePlanItem>>  ServicePlanItem 门店方案明细
     */
    private void nonInnerHelpProcess(List<Map.Entry<RetailReturnItem, ServicePlanItem>> list) {
        // 非内协的部分，需要检查是否为油漆（同公司油漆）
        list.stream().collect(Collectors.groupingBy(item -> item.getValue().isPaintFlag()))
                .forEach((isPaint, itemEntry) -> {
                    if (isPaint) throw new RuntimeException("经销退货时，出现本公司经销油漆退货，需要检查数据");

                    // 经销带安部分（外协）
                    List<RetailReturnItem> retailReturnItems = itemEntry.stream().map(Map.Entry::getKey).collect(Collectors.toList());
                    returnAccountService.returnServerOutHelp(retailReturnItems);
                });
    }

    /**
     * 内协部分处理
     * 如果是经销带安，就说明一定是存在内协或者外协（本公司外协）
     * 服务方案明细中会记录当前商品是，一般，内协，外协
     *
     * @param list List<Map.Entry<RetailReturnItem, ServicePlanItem>>  ServicePlanItem 门店方案明细
     */
    private void innerHelpProcess(List<Map.Entry<RetailReturnItem, ServicePlanItem>> list) {
        // 根据库存位置分组
        Map<Location, List<Map.Entry<RetailReturnItem, ServicePlanItem>>> locationListMap = list.stream().collect(Collectors.groupingBy(x -> x.getKey().getLocation()));

        locationListMap.forEach((location, entries) -> {
            // 判断是否同公司內协油漆商品, 由于同公司内协的部分，ProductRule 不为 10 代销带安 或者 20 经销带安（同公司不写带安凭证）
            entries.stream().collect(Collectors.groupingBy(item -> item.getValue().isPaintFlag()))
                    .forEach((isPaint, item) -> {
                        if (isPaint) {
                            // 同公司油漆退货
                            List<RetailReturnItem> retailReturnItems = item.stream().map(Map.Entry::getKey).collect(Collectors.toList());
                            returnAccountService.returnServerPaint(retailReturnItems);
                        } else {

                            // 这里需要区分是同公司还是跨公司内协
                            HashMap<ServicePlanItem, RetailReturnItem> storeMap = new HashMap();  // 门店退货明细对应的方案明细
                            Map<RetailReturnItem, ServicePlanItem> baseMap = new HashMap();  // 基地退货明细对应的门店方案明细

                            item.forEach(entry -> {
                                // 通过门店方案找到基地方案
                                ServicePlanItem baseServicePlanItem = findBaseServicePlanItem(entry.getValue().getId());

                                String storeCompany = entry.getValue().getServicePlan().getBelongDept().getCompany().getId();
                                String baseCompany = baseServicePlanItem.getServicePlan().getBelongDept().getCompany().getId();
                                if (storeCompany.equals(baseCompany)) {
                                    storeMap.put(baseServicePlanItem, entry.getKey());      // 同公司内协部分
                                } else {
                                    baseMap.put(entry.getKey(), entry.getValue());       // 跨公司内协部分
                                }
                            });

                            // 同公司区分是否有外协
                            returnSameCompany(storeMap);

                            // 跨公司走内协退货，跨公司内协只能转一次
                            returnCrossCompany(baseMap);
                        }
                    });
        });

    }


    /**
     * 同公司内协退货
     * 处理外协部分
     *
     * @param storeMap 同公司基地方案明细
     */
    private void returnSameCompany(Map<ServicePlanItem, RetailReturnItem> storeMap) {
        if (storeMap.isEmpty()) return;
        storeMap.entrySet().stream()
                .collect(Collectors.groupingBy(m -> m.getKey().getInventoryBatch() != null && m.getKey().getSupplier() != null))
                .forEach((isOutHelp, entry) -> {
                    if (!isOutHelp) return;

                    // 查询对应的工单合同，根据合同分组
                    List<ServicePlanItem> baseServicePlanItems = entry.stream().map(Map.Entry::getKey).collect(Collectors.toList());

                    baseServicePlanItems.stream()
                            .collect(Collectors.groupingBy(this::getMaintenanceOrderContract))
                            .forEach((contract, servicePlanItems) -> {

                                List<RetailReturnItem> retailReturnItems = servicePlanItems.stream().map(storeMap::get).collect(Collectors.toList());

                                Map<String, Object> param = new HashMap();
                                param.put("contract", contract);
                                param.put("retailReturnItems", retailReturnItems);
                                returnAccountService.returnServerOutHelp(param);
                            });
                });
    }

    /**
     * 根据服务方案获取工单上面的合同
     *
     * @param servicePlanItem 服务方案
     * @return Contract
     */
    private Contract getMaintenanceOrderContract(ServicePlanItem servicePlanItem) {
        MaintenanceOrder maintenanceOrder = maintenanceOrderRepository.findFirstByServicePlanItemAndBusinessTypeNotIn(servicePlanItem, Collections.singletonList("out"));
        return maintenanceOrder.getContract();
    }

    /**
     * 跨公司内协退货
     * 内协退货单并发起 OA
     *
     * @param baseMap 跨公司门店方案明细
     */
    public InternalHelpReturnApproval returnCrossCompany(Map<RetailReturnItem, ServicePlanItem> baseMap) {
        if (baseMap.isEmpty()) return null;
        List<RetailReturnItem> retailReturnItems = new ArrayList<>(baseMap.keySet());
        RetailReturn retailReturn = retailReturnItems.get(0).getOrder();
        Voucher source = findByVoucherId(retailReturn.getSaleOrder().getId());
        Map<String, VoucherItem> voucherItemMap = source.getVoucherItems().stream().collect(Collectors.toMap(VoucherItem::getSourceItemId, voucherItem -> voucherItem));

        InternalHelpReturnApproval internalHelpReturnApproval = new InternalHelpReturnApproval();
        internalHelpReturnApproval.setCompany(retailReturn.getCompany());
        internalHelpReturnApproval.setDept(retailReturn.getDept());
        internalHelpReturnApproval.setCustomerExpand(retailReturn.getSaleOrder().getCustomerExpand());
        internalHelpReturnApproval.setShipLocation(retailReturnItems.get(0).getLocation());
        internalHelpReturnApproval.setReceiptLocation(getServiceLocation(baseMap.get(retailReturnItems.get(0))));
        internalHelpReturnApproval.setScene(SpringManager.getBean(SceneRepository.class).findOne("SR005")); //注入场景

        Enumeration returnReason = new Enumeration();
        returnReason.setId("inner_return");
        internalHelpReturnApproval.setReturnReason(returnReason);
        internalHelpReturnApproval.setState("sent");
        internalHelpReturnApproval.setNote(retailReturn.getReturnReason());

        internalHelpReturnApproval.setSource(retailReturn.getInnerBusinessKey());
        internalHelpReturnApproval.setSourceBusinessType(retailReturn.getInnerBusinessType());
        internalHelpReturnApproval.setApplyUid(retailReturn.getApproveUID());
        internalHelpReturnApproval.setDateApply(new Date());
        internalHelpReturnApproval.setProcessInitiator(retailReturn.getProcessInitiator());
        internalHelpReturnApproval.setProcessInitiatingDepartment(retailReturn.getProcessInitiatingDepartment());

        List<SaleOrderItem> saleOrderItems = new ArrayList<>();
        internalHelpReturnApproval.setApprovalItems(
                retailReturnItems.stream().map(entry -> {
                    InternalHelpReturnApprovalItem item = new InternalHelpReturnApprovalItem();
                    item.setProduct(entry.getProduct());
                    item.setCompany(retailReturn.getCompany());
                    InventoryBatch inventoryBatch = getInventoryBatchByVoucher(source, String.valueOf(voucherItemMap.get(entry.getRetailOrderItem().getId()).getId()));
                    item.setSupplier(inventoryBatch.getSupplier());
                    item.setInventoryBatch(inventoryBatch);
                    item.setUom(inventoryBatch.getUom());
                    item.setTax(inventoryBatch.getTax());
                    item.setPriceUnit(inventoryBatch.getTaxCostPrice());
                    item.setProductSumQty(inventoryBatch.getCanReturnQty());
                    item.setProductReturnQty(entry.getUom().computeQty(entry.getProductUomQty(), entry.getProduct().getUom(), true));
                    item.setSourceItemId(entry.getId());   // 零售退货单明细行号

                    SaleOrderItem saleOrderItem = getSaleOrderItem(item);       // 将明细行存起来，用与获取分销单号
                    saleOrderItems.add(saleOrderItem);
                    item.setOriginItemId(saleOrderItem.getId());                // 分销单的明细行号
                    return item;
                }).collect(Collectors.toSet()));

        // 获取分销单
        internalHelpReturnApproval.setOrigin(saleOrderItems.get(0).getSaleOrder().getId());
        internalHelpReturnApproval.setOriginBusinessType(SaleOrder.class.getSimpleName());

        return invoke("internal.help.return.apply",
                MapUtil.mapped(invoke("internal.help.return.create", MapUtil.mapped(internalHelpReturnApproval))));
    }

    /**
     * 服务方案项目验收检查, 找到对应内协的部分
     *
     * @param itemHashMap HashMap<RetailReturnItem, ServicePlanItem>
     */
    private Map<Boolean, List<Map.Entry<RetailReturnItem, ServicePlanItem>>> servicePlanItemInnerCheck(HashMap<RetailReturnItem, ServicePlanItem> itemHashMap) {
        return itemHashMap.entrySet().stream().collect(Collectors.groupingBy(map -> Objects.equals(map.getValue().getAssistType().getId(), "ServicePlanItem.assistType.inner")));
    }

    /**
     * 根据门店方案明细查找基地方案明细
     *
     * @param id 门店方案明细编码
     * @return ServicePlanItem
     */
    private ServicePlanItem findBaseServicePlanItem(String id) {
        List<ServicePlanItem> list = servicePlanItemRepository.findAll(JpaDsl.toCriteriaByEq("parentServicePlanItem.id", id, "terminationDate", null));
        if (list.size() == 0) throw new RuntimeException(String.format("根据零售明细单来源行号[%s]获取子服务方案明细行失败！", id));
        if (list.size() > 1) throw new RuntimeException("找到多条子明细行，请检查程序");
        return list.stream().findFirst().get();
    }

    private Voucher findByVoucherId(String id) {
        Voucher source = voucherRepository.findOne(JpaDsl.toCriteriaByEq("sourceBusinessType", RetailOrder.class.getSimpleName(),
                "source", id, "origin", id, "voucherType", "CUSTOMER_SALE"))
                .orElse(null);
        return Optional.ofNullable(source).orElseThrow(() -> new RuntimeException("没有找到退货原单：" + id + " 的业务凭证！"));
    }

    private InventoryBatch getInventoryBatchByVoucher(Voucher voucher, String id) {
        List<VoucherInventory> voucherInventories = SpringManager.getBean(VoucherInventoryRepository.class)
                .findAll(JpaDsl.toCriteriaByEq("voucher.id", voucher.getId(), "voucherItem.id", id));
        if (voucherInventories.isEmpty()) {
            throw new RuntimeException(String.format("无法找到凭证[%s]明细[%s]的价值库存批次", voucher.getId(), id));
        }
        return voucherInventories.stream().findFirst().map(VoucherInventory::getInventoryBatch)
                .orElseThrow(() -> new RuntimeException(String.format("无法找到凭证[%s]明细[%s]的商品价值库存", voucher.getId(), id)));
    }

    /**
     * 查询对应的方案
     *
     * @param servicePlanItem 基地方案明细
     * @return
     */
    private Location getServiceLocation(ServicePlanItem servicePlanItem) {
        ServicePlanItem baseServicePlanItem = findBaseServicePlanItem(servicePlanItem.getId());
        if (baseServicePlanItem.getLocation() == null) {
            List<Location> locations = SpringManager.getBean(LocationRepository.class)
                    .findAll(JpaDsl.toCriteriaByEq("depot.dept.id", baseServicePlanItem.getServicePlan().getBelongDept().getId(), "usage.id", "1"));
            return locations.get(0);
        }
        return baseServicePlanItem.getLocation();
    }

    private SaleOrderItem getSaleOrderItem(InternalHelpReturnApprovalItem internalHelpReturnApprovalItem) {
        // 通过商品库存价值 找采购凭证
        VoucherItem vItem = voucherItemRepository.findOne(internalHelpReturnApprovalItem.getInventoryBatch().getBatchNumber());
        Optional.ofNullable(vItem.getSupplierRef()).orElseThrow(() -> new RuntimeException("通过商品库存价值未找到记录有內协销售的采购凭证" + internalHelpReturnApprovalItem.getInventoryBatch().getId()));

        // 服务方案采购凭证上面的 supplierRef 就是分销单的明细行号
        SaleOrderItem saleOrderItem = SpringManager.getBean(SaleOrderItemRepository.class).findOne(vItem.getSupplierRef());
        Optional.ofNullable(saleOrderItem).orElseThrow(() -> new RuntimeException("获取分销单失败！采购入库凭证" + vItem.getVoucher().getId() + "约定好的参考字段(分销明细)为" + vItem.getSupplierRef()));
        return saleOrderItem;
    }
}
